package com.ruge.tool.lambda;

import com.ruge.tool.str.StringTool;
import lombok.extern.slf4j.Slf4j;

import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

/**
 * @author ruge.wu
 * @since 2021/12/24 15:59
 */
@Slf4j
public class LambdaTool {

    private static final Map<Class, SerializedLambda> CLASS_LAMDBA_CACHE = new ConcurrentHashMap<>();


    /**
     * 转换方法引用为属性名
     *
     * @param fn  入参
     * @param <T> 泛型
     * @return 属性名
     */
    public static <T> String convertToFieldName(R<T> fn) {
        SerializedLambda lambda = getSerializedLambda(fn);
        String methodName = lambda.getImplMethodName();
        String prefix = null;
        String keywordGet = "get";
        String keywordIs = "is";
        if (methodName.startsWith(keywordGet)) {
            prefix = "get";
        } else if (methodName.startsWith(keywordIs)) {
            prefix = "is";
        }
        if (prefix == null) {
            log.warn("无效的getter方法: " + methodName);
        }
        // 截取get/is之后的字符串并转换首字母为小写（S为diboot项目的字符串工具类，可自行实现）
        return StringTool.toLowerCaseFirstOne(methodName.replace(prefix, ""));
    }


    /**
     * 关键在于这个方法
     *
     * @param fn 序列化参数
     * @return 序列化
     */
    public static SerializedLambda getSerializedLambda(Serializable fn) {
        SerializedLambda lambda = CLASS_LAMDBA_CACHE.get(fn.getClass());
        if (lambda == null) {
            try {
                Method method = fn.getClass().getDeclaredMethod("writeReplace");
                method.setAccessible(Boolean.TRUE);
                lambda = (SerializedLambda) method.invoke(fn);
                CLASS_LAMDBA_CACHE.put(fn.getClass(), lambda);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return lambda;
    }


    /**
     * 这里说明一下，我们每次传入forEach都是一个重新实例化的Consumer对象，在lambada表达式中我们无法对int进行++操作,
     * 我们模拟AtomicInteger对象，写个getAndIncrement方法，不能直接使用AtomicInteger哦
     *
     * @param biConsumer 利用BiConsumer实现foreach循环支持index
     * @param <T>        泛型
     * @return index
     */
    public static <T> Consumer<T> forEachWithIndex(BiConsumer<T, Integer> biConsumer) {
        class IncrementInt {
            int i = 0;

            public int getAndIncrement() {
                return i++;
            }
        }
        IncrementInt incrementInt = new IncrementInt();
        return t -> biConsumer.accept(t, incrementInt.getAndIncrement());
    }
}
